/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.controller.evaluation.analyze.correction;

import cn.ac.nercis.pes.common.constant.knowledge.LayerTarget;
import cn.ac.nercis.pes.community.component.cells.NumberTableCell;
import cn.ac.nercis.pes.community.component.controls.MenuListCell;
import cn.ac.nercis.pes.community.component.dialog.*;
import cn.ac.nercis.pes.community.model.vo.MenuVO;
import cn.ac.nercis.pes.community.model.vo.TableDataModel;
import cn.ac.nercis.pes.community.model.vo.evaluation.ResultCorrectionVO;
import cn.ac.nercis.pes.community.support.event.CommonCallback;
import cn.ac.nercis.pes.community.utils.ControlUtils;
import cn.ac.nercis.pes.community.utils.LogUtils;
import cn.ac.nercis.pes.community.utils.SpringUtils;
import cn.ac.nercis.pes.model.dto.evaluation.analyze.OffsetAssetDTO;
import cn.ac.nercis.pes.service.evaluation.ResultCorrectionService;
import javafx.application.Platform;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.URL;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * 弥补对象管理控制器
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class OffsetAssetController implements Initializable {

    @FXML
    private StackPane offsetAssetRoot;
    @FXML
    private VBox offsetAssetPane;
    @FXML
    private VBox emptyPane;
    @FXML
    private ImageView emptyImageView;
    @FXML
    private Label emptyLabel;

    @FXML
    private VBox selectOffsetAssetPane;
    @FXML
    private Label assetNameLbl;
    @FXML
    private Label targetNameLbl;
    @FXML
    private Label resultLbl;
    @FXML
    private Label scoreLbl;

    @FXML
    private ListView<MenuVO> menuListView;
    @FXML
    private TableView<OffsetAssetDTO> offsetAssetTableView;
    @FXML
    private TableColumn<OffsetAssetDTO,String> numberColumn;
    @FXML
    private TableColumn<OffsetAssetDTO,String> targetColumn;
    @FXML
    private TableColumn<OffsetAssetDTO,String> assetNameColumn;
    @FXML
    private TableColumn<OffsetAssetDTO,String> assetResultColumn;
    @FXML
    private TableColumn<OffsetAssetDTO,String> assetScoreColumn;

    private final ResultCorrectionService resultCorrectionService;
    private final ExecutorService executor;
    private final TableDataModel<OffsetAssetDTO> dataModel = new TableDataModel<>();
    private List<OffsetAssetDTO> offsetAssets;
    private LayerTarget currentLayerTarget;
    private CommonCallback delOffsetAsset;


    public OffsetAssetController() {
        this.resultCorrectionService = SpringUtils.getBean(ResultCorrectionService.class);
        this.executor = SpringUtils.getBean(ExecutorService.class);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        offsetAssetPane.managedProperty().bind(offsetAssetPane.visibleProperty());
        emptyPane.managedProperty().bind(emptyPane.visibleProperty());
        selectOffsetAssetPane.managedProperty().bind(selectOffsetAssetPane.visibleProperty());
        initTable();

        menuListView.setCellFactory(cf->new MenuListCell());
        menuListView.getSelectionModel()
                .selectedItemProperty()
                .addListener((observable,oldValue,newValue)-> {
                    if(Objects.nonNull(newValue)){
                        currentLayerTarget = LayerTarget.fromName(newValue.getTitle());
                        loadContent();
                    }
                });
        ControlUtils.initEmptyImageView(this.emptyImageView,log);
    }

    public void initializeData(@NonNull ResultCorrectionVO resultCorrectionVO) {
        LogUtils.info(log, "初始化弥补对象管理内容...");
        if (StringUtils.isNotBlank(resultCorrectionVO.getOffsetRecordId())) {
            selectOffsetAssetPane.setVisible(true);
            loadSelectedOffsetAssetData(resultCorrectionVO.getOffsetRecordId());
        } else {
            selectOffsetAssetPane.setVisible(false);
        }
        loadOffsetAssetData(resultCorrectionVO);
    }

    public void setDelOffsetAsset(CommonCallback delOffsetAsset) {
        this.delOffsetAsset = delOffsetAsset;
    }

    @FXML
    public void onDelRelatedClick() {
        AlertDialog alertDialog = AlertDialog.builder()
                .withMode(AlertMode.CONFIRM)
                .withType(AlertType.WARN)
                .withTitle("删除提示")
                .withContent("确认删除当前已选弥补对象吗？")
                .withContainer(offsetAssetRoot)
                .build();
        alertDialog.show((confirm) -> {
            if (!confirm) {
                return;
            }
            if (Objects.nonNull(this.delOffsetAsset)) {
                this.delOffsetAsset.action();
            }
        });
    }

    public OffsetAssetDTO getSelected() {
        if (dataModel.getSelectedIndex() < 0) {
            MessageDialog.builder()
                    .withContainer(offsetAssetRoot)
                    .withType(MessageType.WARN)
                    .withMessage("请选择弥补的对象")
                    .build().show();
            return null;
        }
        return dataModel.getSelectedItem();
    }

    private void initTable(){
        numberColumn.setCellFactory(col-> new NumberTableCell<>());
        targetColumn.setCellValueFactory(new PropertyValueFactory<>("unitTargetName"));
        assetNameColumn.setCellValueFactory(new PropertyValueFactory<>("assetName"));
        assetResultColumn.setCellValueFactory(new PropertyValueFactory<>("assetResult"));
        assetScoreColumn.setCellValueFactory(new PropertyValueFactory<>("assetScore"));
        offsetAssetTableView.setItems(new FilteredList<>(dataModel.bodyProperty(), p->true));

        dataModel.selectedIndexProperty().bind(offsetAssetTableView.getSelectionModel().selectedIndexProperty());
    }

    private void loadSelectedOffsetAssetData(String offsetResultId) {
        LogUtils.info(log, "获取关联的弥补对象列表...");
        LogUtils.record(log, "请求数据:{}", offsetResultId);
        CompletableFuture.supplyAsync(() -> resultCorrectionService.getOffsetAssets(offsetResultId),this.executor)
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        initSelectedOffset(res.getData());
                    } else {
                        MessageDialog.builder()
                                .withContainer(offsetAssetRoot)
                                .withType(MessageType.FAIL)
                                .withMessage("未获取到已选择的弥补对象信息")
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("获取关联的弥补对象列表出错!", ex);
                    return null;
                });
    }

    private void loadOffsetAssetData(ResultCorrectionVO resultCorrectionVO) {
        LogUtils.info(log, "获取关联的弥补对象列表...");
        LogUtils.record(log, "请求数据，systemId：{},assetId:{},unitTargetId:{}", resultCorrectionVO.getSystemId(),
                resultCorrectionVO.getAssetId(), resultCorrectionVO.getUnitTargetId());
        CompletableFuture.supplyAsync(() -> resultCorrectionService.findOffsetAssets(resultCorrectionVO.getSystemId(),
                        resultCorrectionVO.getAssetId(), resultCorrectionVO.getUnitTargetId()),this.executor)
                .thenAccept(res -> Platform.runLater(() -> {
                    if (CollectionUtils.isNotEmpty(res)) {
                        offsetAssetPane.setVisible(true);
                        emptyPane.setVisible(false);
                        initOffsetAssetContent(res);
                    } else {
                        offsetAssetPane.setVisible(false);
                        emptyPane.setVisible(true);
                        emptyLabel.setText("无符合条件的弥补对象");
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("获取关联的弥补对象列表出错!", ex);
                    return null;
                });
    }

    private void initSelectedOffset(OffsetAssetDTO assetDTO){
        assetNameLbl.setText(assetDTO.getAssetName());
        targetNameLbl.setText(assetDTO.getLayerTargetName().concat("/").concat(assetDTO.getUnitTargetName()));
        resultLbl.setText(assetDTO.getAssetResult());
        scoreLbl.setText(assetDTO.getAssetScore());
    }

    private void initOffsetAssetContent(List<OffsetAssetDTO> offsetAssets){
        List<LayerTarget> layerTargets = offsetAssets.stream()
                .map(OffsetAssetDTO::getLayerTargetName)
                .map(LayerTarget::fromName)
                .distinct()
                .sorted(Comparator.comparingInt(LayerTarget::getValue))
                .collect(Collectors.toList());
        this.offsetAssets = offsetAssets;
        initOffsetMenu(layerTargets);
    }

    private void initOffsetMenu(List<LayerTarget> layerTargets){
        menuListView.getItems().clear();
        for(LayerTarget menu : layerTargets){
            menuListView.getItems().add(new MenuVO(menu.getValue(),"",menu.getName(),"folder-icon"));
        }
        menuListView.getSelectionModel().select(0);
    }

    private void loadContent(){
        if(Objects.isNull(currentLayerTarget)){
            return;
        }
        dataModel.bodyProperty().clear();
        offsetAssets.stream()
                .filter(oa->StringUtils.equals(oa.getLayerTargetName(),currentLayerTarget.getName()))
                .forEach(oa->dataModel.bodyProperty().add(oa));
    }
}